import { ref, computed, nextTick, onUnmounted, type Ref } from "vue";

export interface ScrollListItem {
    id: string | number;
    [key: string]: any;
}

export interface UseScrollListOptions<T extends ScrollListItem> {
    itemHeight: number;
    buffer?: number;
    fetchPage: (page: number, pageSize: number) => Promise<T[]> | T[];
    pageSize?: number;
    realtimeFactory?: () => T;
    realtimeInterval?: number;
}

export function useScrollList<T extends ScrollListItem>(rootEl: Ref<HTMLElement | null>, anchorEl: Ref<HTMLElement | null>, options: UseScrollListOptions<T>) {
    const { itemHeight, buffer = 5, pageSize = 20, fetchPage, realtimeFactory, realtimeInterval = 3000 } = options;
    /* ---- 分页状态 ---- */
    const list = ref<T[]>([]);
    const pageNo = ref(0);
    const isLastPage = ref(false);
    const loading = ref(false);

    /* ---- 虚拟滚动 ---- */
    const scrollTop = ref(0);
    const clientHeight = ref(0);

    const startIndex = computed(() => Math.max(0, Math.floor(scrollTop.value / itemHeight) - buffer));
    const endIndex = computed(() => Math.min(list.value.length - 1, Math.floor((scrollTop.value + clientHeight.value) / itemHeight) + buffer));
    const visibleList = computed(() =>
        list.value.slice(startIndex.value, endIndex.value + 1).map((raw, i) => ({
            ...raw,
            offsetY: (startIndex.value + i) * itemHeight,
        }))
    );
    const totalHeight = computed(() => list.value.length * itemHeight);

    /* ---- 无限分页 ---- */
    async function loadNextPage() {
        if (loading.value || isLastPage.value) return;
        loading.value = true;
        try {
            const newList = await fetchPage(pageNo.value + 1, pageSize);
            if (!newList.length) isLastPage.value = true;
            else {
                list.value.push(...(newList as any));
                pageNo.value++;
            }
        } finally {
            loading.value = false;
        }
    }

    /* ---- 滚动监听 ---- */
    function onBodyScroll(e: Event) {
        scrollTop.value = (e.target as HTMLElement).scrollTop;
    }

    /* ---- IntersectionObserver ---- */
    let io: IntersectionObserver | null = null;
    let timer: number | null = null;

    function init() {
        if (!rootEl.value || !anchorEl.value) return;
        clientHeight.value = rootEl.value.querySelector(".table-body")!.clientHeight;

        io = new IntersectionObserver(([entry]) => entry.isIntersecting && loadNextPage(), { root: rootEl.value.querySelector(".table-body") });
        io.observe(anchorEl.value);
        loadNextPage();

        if (realtimeFactory && realtimeInterval > 0) {
            if (timer) {
                clearInterval(timer);
            }
            timer = window.setInterval(() => {
                list.value.push(realtimeFactory());
                nextTick(() => {
                    const body = rootEl.value!.querySelector(".table-body")!;
                    body.scrollTo({ top: body.scrollHeight, behavior: "smooth" });
                });
            }, realtimeInterval);
        }
    }

    onUnmounted(() => {
        io?.disconnect();
        if (timer) clearInterval(timer);
    });

    return {
        list,
        visibleList,
        totalHeight,
        onBodyScroll,
        init,
    };
}
